在GOF 23種設計模式中,橋接模式屬於結構型而策略模式屬於行為型的設計模式。
雖然在定義及用途上有些許不同,但設計上的架構有著相似之處。
抽象部分與它的具體實現部分分離,使它們都可以獨立地變化。
看著這個定義,我相信十有八九的人應該是都會放棄這個模式了,
所以我們這邊就簡單地用自己的語言幫她翻譯一下:
** 透過使用傳入內部的物件(Object)來完成方法(Method)的內容 **
對於橋接模式用途的解釋是:
可以用不同的工具,來完成一樣的結果。
最常見的例子像是兼容不同的資料庫的資料索取,或是跨平台的畫面渲染...等。
將一系列的算法封裝起來,並且在運行時可以替換使用。
實作上的概念其實也跟前面一樣:
** 透過使用傳入內部的物件(Object)來完成方法(Method)的內容 **
(不一樣的地方最後在解釋)
策略模式主要是解決,在同一個流程下,不同條件所產生的變化
例如:遊戲中技能傷害的計算方式(戰士與法師....等各職業的算法不同),
或是公司內部KPI對於薪資的影響(工程師、專案經理...等職位的算法不同)。
與其說一大堆理論,不如看個幾行程式碼...
假設今天我們有兩個需求
需要將員工的KPI資料撈出來,但因為公司內部還未將資料正規化。
所以經理的資料在資料庫內,工程師則是.txt
的文件
用剛剛取得的KPI計算公司員工的薪水,依照不同的職位來計算:
這樣的問題我們應該如何設計呢?
//定義 員工資訊的介面
interface IStaffInfo{
public string selectName();
public string selectTitle();
public int selectKPI();
}
//分別實作用 DB 及 TxT 方式取得員工資訊的物件
class DBStaffInfo : IStaffInfo{
public string selectName(){
string name ;
//TODO: ...從資料庫取得職員 姓名...
return name ;
}
public string selectTitle(){
string title ;
//TODO: ...從資料庫取得職員 職稱...
return title ;
}
public int selectKPI(){
string KPI ;
//TODO: ...從資料庫取得職員 績效...
return KPI ;
}
}
class TxtStaffInfo : IStaffInfo{
public string selectName(){
string name ;
//TODO: ...從文件檔取得職員 姓名...
return name ;
}
public string selectTitle(){
string title ;
//TODO: ...從文件檔取得職員 職稱...
return title ;
}
public int selectKPI(){
string KPI ;
//TODO: ...從文件檔取得職員 績效...
return KPI ;
}
}
//定義好需求上的 員工抽象類別
abstract class AbstractStaff{
private IStaffInfo info;
//職員 名稱
public abstract string getName();
//職員 職稱
public abstract string getTitle();
//職員 績效
public abstract int getKPI();
}
//實作員工類別
abstract class Staff : AbstractStaff{
private IStaffInfo info;
private string name ;
private string title;
private int KPI;
//傳入基本資料
public Staff(IStaffInfo info){
this.info = info;
}
//從info中取得 職員名稱 的資料
public string getName(){
name = info.selectName();
return name;
}
//從info中取得 職員職位 的資料
public string getTitle(){
title = info.selectTitle();
return title;
}
//從info中取得 職員績效 的資料
public int getKPI(){
KPI = info.selectKPI();
return KPI;
}
}
我們接著設計員工薪資的計算方式
//定義好介面
interface ICalculateSalaryStrategy{
public int calculate(int KPI);
}
//主管 的薪資計算策略
class ManagerCalculateStrategy{
public int calculate(int KPI){
return 20000 + 10000 * KPI;
}
}
//工程師 的薪資計算策略
class EngineerCalculateStrategy{
public int calculate(int KPI){
return 50000 + 5000 * KPI;
}
}
最後我們來看看主流程main()
的部分:
public static void main(){
//----- 橋接模式 -----
IStaffInfo db_info = new DBStaffInfo();
IStaffInfo txt_info = new TxtStaffInfo();
//新增兩個員工
//並分別透過 DB、Txt兩種方式取得資料
AbstractStaff manager = new Staff(db_info);
AbstractStaff engineer = new Staff(txt_info);
// ================
//----- 策略模式 -----
ICalculateSalaryStrategy calculate;
//判斷應該要用何種方式計算薪水
if (manager.getTitle() == 'manager'){
calculate = new ManagerCalculateStrategy();
}
else if(manager.getTitle() == 'engineer'){
calculate = new EngineerCalculateStrategy();
}
//計算主管的薪水
calculate.calculate(KPI);
}
橋接模式: 在同一個結構下用不同的工具,也要有相同的輸出 (不管用DB、TXT都要得到員工資料)
策略模式: 在同一個流程下用不同的方法,得到不同的答案 (同樣都是計算薪水,但是希望有不同的計算方式)
橋接模式: 通常會負責處理執行以前的變因(資料取得要從DB 還是 Txt開始執行以前就知道了)
策略模式: 通常處理執行以後才會發生的變因(要執行哪一種策略要在流程中有對應條件的時候才知道)
橋接模式: 在結構上將下層的工具類與上層的應用分開(取得職員資料 與 使用職員資料 分開)
策略模式: 將下層選擇性結構轉到上層處理,保持下層處理業務上層處理流程(calculate負責計算、main負責決定用甚麼方法 )
個人認為這兩種模式在本質上是一樣的。
所以在學習的過程中,不必過於區分兩者的區別。
只需要了解在這樣的設計架構底下有甚麼樣的應用情境。
其實,還有一種設計模式(狀態模式)也與這兩者很類似,
不過這個設計模式我們就留到之後的章節再說。